// @Author karl
// @Date 2025/9/20 14:13:00
// @Desc
package middleware

import (
	"bytes"
	"encoding/json"
	"g_group-buy-market/internal/pkg/config"
	"g_group-buy-market/pkg/globalService"
	"g_group-buy-market/pkg/utils"
	"github.com/bytedance/gopkg/util/logger"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"io"
	"net/http"
	"strings"
	"time"
)

// httpLogConfig 日志配置结构
type httpLogConfig struct {
	// 是否记录请求体
	LogRequestBody bool
	// 是否记录响应体
	LogResponseBody bool
	// 需要排除的请求路径
	ExcludePaths []string
	// 需要排除的请求头
	ExcludeReqHeaders []string
	// 需要排除的响应头
	ExcludeResHeaders []string
	// 敏感字段，记录时会进行脱敏处理
	SensitiveFields []string
	// 自定义日志记录器
	Logger *zap.Logger
	// 日志级别
	LogLevel logger.Level
}

// RequestTracer 请求追踪中间件
type bodyLogWriter struct {
	gin.ResponseWriter
	body *bytes.Buffer
}

func (w bodyLogWriter) Write(b []byte) (int, error) {
	w.body.Write(b)
	return w.ResponseWriter.Write(b)
}

// 进行配置
func HttpLogConfig(config *config.Config, zapLog *zap.Logger) *httpLogConfig {
	return &httpLogConfig{
		LogRequestBody:    config.HttpLog.LogRequestBody,
		LogResponseBody:   config.HttpLog.LogResponseBody,
		ExcludePaths:      config.HttpLog.ExcludePaths,
		ExcludeReqHeaders: config.HttpLog.ExcludeReqHeaders,
		ExcludeResHeaders: config.HttpLog.ExcludeResHeaders,
		SensitiveFields:   config.HttpLog.SensitiveFields,
		Logger:            zapLog,
		LogLevel:          logger.LevelInfo,
	}
}

func HttpRequestTracerLogger(config *httpLogConfig) gin.HandlerFunc {
	return func(c *gin.Context) {
		// 判断是否需要记录日志
		for _, path := range config.ExcludePaths {
			if strings.HasPrefix(c.Request.URL.Path, path) {
				c.Next()
				return
			}
		}
		// 只在当次请求中进行获取
		c.Set("request_id", utils.GenerateLogId())
		start := time.Now()
		var reqBody []byte
		if config.LogRequestBody && c.Request.Body != nil {
			reqBody, _ = io.ReadAll(c.Request.Body)
			c.Request.Body = io.NopCloser(bytes.NewBuffer(reqBody))
			// 敏感信息脱敏
			reqBody = maskSensitiveFields(reqBody, config.SensitiveFields)
		}

		// 捕捉相应体
		var resBody *bytes.Buffer
		var resWriter gin.ResponseWriter
		reqHeader := copyHeader(c.Request.Header, config.ExcludeReqHeaders)
		if config.LogResponseBody {
			resBody = bytes.NewBufferString("")
			resWriter = &bodyLogWriter{body: resBody, ResponseWriter: c.Writer}
			c.Writer = resWriter
		}
		// 处理请求
		c.Next()
		latency := time.Since(start)
		statusCode := c.Writer.Status()
		resHeader := copyHeader(c.Writer.Header(), config.ExcludeResHeaders)
		var responseBody []byte
		if config.LogResponseBody && resBody != nil {
			responseBody = resBody.Bytes()
			responseBody = maskSensitiveFields(responseBody, config.SensitiveFields)
		}
		// 记录日志
		fields := []zap.Field{
			zap.String("timestamp", start.Format(time.DateTime)),
			zap.String("method", c.Request.Method),
			zap.String("path", c.Request.URL.Path),
			zap.String("query", c.Request.URL.RawQuery),
			zap.String("client", c.ClientIP()),
			zap.String("userAgent", c.Request.UserAgent()),
			zap.Int("statusCode", statusCode),
			zap.String("latency", latency.String()),
			zap.String("logId", c.GetString("request_id")),
			zap.Any("request_headers", reqHeader),
			zap.Any("response_headers", resHeader),
		}
		if config.LogRequestBody && len(reqBody) > 0 {
			fields = append(fields, zap.String("request_body", string(reqBody)))
		}
		if config.LogResponseBody && len(responseBody) > 0 {
			fields = append(fields, zap.String("response_body", string(responseBody)))
		}
		// 根据状态码记录不同级别的日志
		if statusCode >= http.StatusInternalServerError {
			globalService.ZapLogClient.Error("服务器错误", fields...)
		} else if statusCode >= http.StatusBadRequest {
			globalService.ZapLogClient.Warn("客户端错误", fields...)
		} else {
			globalService.ZapLogClient.Info("请求成功", fields...)
		}
	}
}

// copyHeader 复制Header并排除不需要的字段
func copyHeader(header map[string][]string, excludeKeys []string) map[string][]string {
	if len(excludeKeys) == 0 {
		return header
	}
	result := make(map[string][]string)
	// 获取请求头的内容
	for k, v := range header {
		excluded := false
		for _, ek := range excludeKeys {
			if k == ek {
				excluded = true
				break
			}
		}
		if !excluded {
			result[k] = v
		}
	}
	return result
}

// maskSensitiveFields 脱敏敏感字段
func maskSensitiveFields(data []byte, sensitiveFields []string) []byte {
	if len(data) == 0 || len(sensitiveFields) == 0 {
		return data
	}
	var jsonData map[string]interface{}
	if err := json.Unmarshal(data, &jsonData); err != nil {
		globalService.ZapLogClient.Error("request_tracer jsonData json.Unmarshal error", zap.Error(err))
		return data
	}
	maskSensitiveData(jsonData, sensitiveFields)

	maskedData, err := json.Marshal(jsonData)
	if err != nil {
		globalService.ZapLogClient.Error("request_tracer maskedData json.Marshal error", zap.Error(err))
		return data
	}
	return maskedData
}

// maskSensitiveData 脱敏数据
func maskSensitiveData(data map[string]interface{}, sensitiveFields []string) {
	for key, value := range data {
		for _, sensitiveField := range sensitiveFields {
			if key == sensitiveField {
				data[key] = "******" + key + "******"
				break
			}
		}

		// 递归处理嵌套的map
		if nested, ok := value.(map[string]interface{}); ok {
			maskSensitiveData(nested, sensitiveFields)
		}

		// 递归处理嵌套的数组
		if array, ok := value.([]interface{}); ok {
			for i, item := range array {
				if nested, ok := item.(map[string]interface{}); ok {
					maskSensitiveData(nested, sensitiveFields)
					array[i] = nested
				}
			}
		}
	}
}
